/*
 *  Openmysee
 *
 *  This program is free software; you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License as published by
 *  the Free Software Foundation; either version 2 of the License, or
 *  (at your option) any later version.
 *
 *  This program is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU General Public License for more details.
 *
 *  You should have received a copy of the GNU General Public License
 *  along with this program; if not, write to the Free Software
 *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 *
 */
				 
#include "echo.h"
struct Argument
{
	int type;
	int spchannelcount;
	int totalclient;
	long long totalupsize;
	FILE *f, *xml[MAX_TS];
	char *buf;
};
#define SPUPDATE_SLOT		1

#define MAX_CHANNEL		1024	/* max number of Channel */

#define SP4CP_PORT		50001

#define TS4RM_PORT		22169
#define RM2TS_STAT_QUERY	0x20
#define TS2RM_STAT_RESPONSE	0x30

int MAX_CP=512;
int MAX_CS=32;

int MAX_JOB_PER_SESSION=3;

char *Home = "./";
char *PREFIX = "/data/sp/";
char *Database = "user.db";
char *CONFIG = "./asp.cfg";
char *PIDFile = "/var/run/spnew.pid";
char *RULE = "";
char *SERVERIP;
char *DBFILE;
char *MEDIABASE;

char *AUTH_MD5;
int AUTH_USERID;

fd_set osocks;
int NumNewChannel;

struct Message TempMsg;

char *NET_NAME[] = { "edu", "cnc", "uni", "tel" };

long long totalDownBytes=0, totalUpBytes=0;
long long tmpDownBytes=0, tmpUpBytes=0;

char *urlroot;
char *defaultspip;
char *spip[4];
char *tsip[4];
#ifdef TEST
char *WWW_ROOT;
#endif
#ifdef HAVE_MYSQL
char *MYSQL_HOST;
char *MYSQL_USER;
char *MYSQL_PASS;
char *MYSQL_DB;
#endif
char *CAS_ADDR;     // Content Aggregation Server, added by lixingwu, 20070313

int tsSock[MAX_TS];
struct sockaddr_in tsAddr[MAX_TS];

extern struct Channel *ChannelHash[MAX_CHANNEL];
extern struct Channel *ChannelList;
extern int isDelete (struct Channel *pc);

extern int errno;

int JobHighWater = 10000;
int BINDALL=1;
int AuthCS=0;
time_t startTime;
time_t CurrentTime;
int PERIOD;
int SnapShotInterval;		//make a snapshot every few second
int spuprecord[MAX_CHANNEL];
struct ServerDesc TRACKER[MAX_TYPE];

char *LOGXML;

struct NamVal ConfigParameters[] = {
	{"DBdir", &Home, 's'},
	{"AuthCS", &AuthCS, 'd'},
	{"MAX_CP",&MAX_CP, 'd'},
	{"MAX_CS", &MAX_CS, 'd'},
	{"Pidfile", &PIDFile, 's'},
	{"Prefix", &PREFIX, 's'},
	{"DBfile", &Database, 's'},
	{"TrackerIP", &SERVERIP, 's'},
	{"Dir", &MEDIABASE, 's'},
	{"authid", &AUTH_USERID, 'd'},
	{"authmd5", &AUTH_MD5, 's'},
	{"periodDump", &PERIOD, 'd'},	//make a periodCheck every periodDump second
	{"UrlRoot", &urlroot, 's'},
	{"EDUSPIP", &spip[EDUTS], 's'},
	{"CNCSPIP", &spip[CNCTS], 's'},
	{"UNISPIP", &spip[UNITS], 's'},
	{"TELSPIP", &spip[TELTS], 's'},
	{"BINDALL", &BINDALL, 'd'},
	{"EDUTSIP", &tsip[EDUTS], 's'},
	{"CNCTSIP", &tsip[CNCTS], 's'},
	{"UNITSIP", &tsip[UNITS], 's'},
	{"TELTSIP", &tsip[TELTS], 's'},
	{"SnapShotInterval", &SnapShotInterval, 'd'},
	{"LogFilePath", &LOGXML, 's'},
#ifdef TEST
	{"WWWRoot", &WWW_ROOT, 's'},
#endif
#ifdef HAVE_MYSQL
	{"MysqlAddress", &MYSQL_HOST, 's'},
	{"User", &MYSQL_USER, 's'},
	{"Database", &MYSQL_DB, 's'},
	{"Password", &MYSQL_PASS, 's'},
#endif
	{"AllowCSList", &RULE, 's'},
	{"JobHighWater", &JobHighWater, 'd'},
    {"CAS_ADDR", &CAS_ADDR, 's'}     // Content Aggregation Server, added by lixingwu, 20070313
};

extern int db_end ();
extern int db_init (char *home, char *database);
extern int isAllowed (int id, char *md5, char *cname, float bitrate,
		      float *limitedbitrate, int *issave);
extern char *getJobBuffer (struct JobDes *p, int *max);
extern inline void setblockId (struct JobDes *pj, int id);
#ifdef TEST
int buildGTV (struct Channel *pc, int datalen, char *data, int type);
#endif
int send_P2P_SPUPDATE (struct Session *p, struct Channel *pc, char *md5, struct SPUpdate *s);
int send_p2p_err (struct Session *p, unsigned short code, int quit);
int init_sp ();
int handle_new_connection (int sock, int type);
int Clientclosure (int listnum, int type);
void process_child (void);
int init_CP (int listnum);
int process_CP (int listnum);
int closure_CP (int listnum);
int init_CS (int listnum);
int process_CS (int listnum);
int closure_CS (int listnum);
#ifdef HAVE_MYSQL
MYSQL *local_mysql;
#endif
int process_P2P_REQUEST_real (struct Session *p, struct Channel *pc, int id);
void apply_check (struct Channel *p, void *arg);
void apply_idle (struct Session *p, void *arg);
int period_process ();
void process_TS2RM (int type);

extern int locate_mplist_by_id (struct Channel *pc, unsigned int id, char *buf, int max);
extern int check_newplist ();
extern int timer_free ();
extern int timer_process (unsigned int t);
extern void hup_handler (int);

#include "sessions.c"

#define BUILD_NOCH_SPUPDATE(s)		do\
{\
	s.minKeySample = -1LL;\
	s.maxKeySample = -1LL;\
	s.minBlockID = 0xffffffff;\
	s.maxBlockID = 0xffffffff;\
} while (0)

#define BUILD_ORDER_SPUPDATE(s,pc)	do\
{\
	s.minKeySample = -1LL;\
	s.maxKeySample = -1LL;\
	s.minBlockID = 0;\
	s.maxBlockID = (pc->downsize > 0 ? ((pc->downsize - 1) / DEFAULT_BLOCK) : 0)+1;\
} while (0)

#define BUILD_CLOSE_SPUPDATE(s)		do\
{\
	s.minKeySample = 0;\
	s.maxKeySample = 0;\
	s.minBlockID = 0xffffffff;\
	s.maxBlockID = 0xffffffff;\
} while (0)

#define BUILD_MLIST_SPUPDATE(s,pc)	do\
{\
	s.minKeySample = -2LL;\
	s.maxKeySample = pc->pcinfo->mlist->m_totalchannel;\
	s.minBlockID = 0;\
	s.maxBlockID = pc->pcinfo->maxID;\
} while (0)

#define BUILD_LIVE_SPUPDATE(spupdate,pc)	do\
{\
	spupdate.minKeySample = -3LL;\
	spupdate.maxKeySample = 1;\
	spupdate.minBlockID = pc->pcinfo->s.minBlockID;\
	spupdate.maxBlockID = pc->pcinfo->s.maxBlockID;\
} while (0)

void send_all_spupdate (struct Channel *pc, struct SPUpdate *s)
{
	struct LiveChannelInfo *pcinfo = pc->pcinfo;
	struct Edge *pedge;
	char buffer[MAX_DATA], *buf;
	if (pcinfo == NULL) return;

	pcinfo->updated = CurrentTime;
	buf = buffer + sizeof (int);
	*(unsigned char *) buf = P2P_SPUPDATE;
	buf += sizeof (char);
	memcpy (buf, pc->channel_md5, MD5_LEN);
	buf += MD5_LEN;
	if (s == NULL)
		memset (buf, 0, sizeof (struct SPUpdate));
	else
		memcpy (buf, s, sizeof (struct SPUpdate));
	buf += sizeof (struct SPUpdate);
	*(int *) buffer = buf - buffer;
	for (pedge = pc->PeerHead; pedge; pedge = pedge->cnext)
	{
		if (writeMessage (pedge->me, pc, buffer) < 0)
		{
			PDEBUG ("send SPUPDATE err.\n");
		}
	}
}

void
process_TS2RM (int type)	//assure num of channel 
{
	int len;
	char buffer[4096];
	char *buf;
	struct sockaddr_in addr;
	int addrlen = sizeof (struct sockaddr);
	int i, msgsize, chnlnum;
	struct Channel *pChannel;

	if ((len =
	     recvfrom (tsSock[type], buffer, 4096, 0,
		       (struct sockaddr *) &addr, &addrlen)) < 0)
	{
		perror ("recvfrom ts");
		return;
	}
	PINFO ("got TS msg, len %d. \n", len);
	buf = buffer;
	msgsize = *(int *) buf;
	buf += sizeof (int);
	if (*(unsigned char *) buf != TS2RM_STAT_RESPONSE)
	{
		perror ("bad message type from ts");
		return;
	}
	buf += sizeof (unsigned char);
	chnlnum = *(int *) buf;
	buf += sizeof (int);
	if (chnlnum > 100)
	{
		perror ("too large channel num from ts");
		return;
	}
	for (i = 0; i < chnlnum; ++i)
	{
		if ((pChannel = findChannel (buf, MD5_LEN)) == NULL)
		{
			buf += MD5_LEN;
			buf += sizeof (int);
			continue;
		}
		buf += MD5_LEN;
		pChannel->numofnp[type] = *(int *) buf;
		buf += sizeof (int);

		PINFO ("%s->%s: client %d.\n",
			pChannel->pcinfo ? pChannel->channel_name : "",
			pChannel->channel_md5, pChannel->numofnp[type]);
	}
}

time_t lastCheck, last_snapshot;

int
period_process ()
{
	int type;
	time_t tmpTime, time_interval;
	struct Argument arg;
	char buffer[MAX_DATA];
	struct tm result;
	int *querynum;
	static int snapCount = 0;

	timer_process (CurrentTime);
	check_newplist ();
	if (CurrentTime <= lastCheck + PERIOD)	//PERIOD =periodDump=60
		return 0;

	memset (&arg, 0, sizeof (arg));
	tmpTime = CurrentTime - startTime;
	time_interval = CurrentTime - lastCheck;
#ifdef HAVE_MYSQL
	query_mysql (local_mysql, "delete from channel where '1'");
#endif
#ifdef TEST
	for (type=0; type<MAX_TS; type++)
	{
		snprintf (buffer, MAX_DATA, "%s/%s/channel.xml", WWW_ROOT,
		  	NET_NAME[type]);
		if ((arg.xml[type] = fopen (buffer, "w")) == (FILE *) 0)
		{
			PDEBUG ("Error in open file %s\n", buffer);
			continue;
		}
		fprintf (arg.xml[type],
		 	"<?xml version=\"1.0\" encoding=\"gbk\"?>\r\n<?xml:stylesheet type=\"text/xsl\" href=\"simple.xsl\" ?>\r\n<GaoV>\r\n");
	}
#endif
	localtime_r (&CurrentTime, &result);
	snprintf (buffer, MAX_DATA, "./sp-%d-%d-%d.log", result.tm_year+1900, result.tm_mon+1, result.tm_mday);
	if ((arg.f = fopen (buffer, "a")) == NULL)
	{
		PDEBUG ("Couldn't open sp log file! \n");
		return -1;
	}
	fprintf (arg.f, "\n\n********************Start %d SnapShot of SP, Time: %u/%u %u:%u:%u.\n",
		snapCount++, result.tm_mon + 1, result.tm_mday, result.tm_hour,
	 	result.tm_min, result.tm_sec);

	fprintf(arg.f, "SP: cur Down %.4f KB. \n", ((float)tmpDownBytes)/1024/time_interval);
	fprintf(arg.f, "SP: cur Up   %.4f KB. \n", ((float)tmpUpBytes)/1024/time_interval);
	totalDownBytes += tmpDownBytes;
	totalUpBytes += tmpUpBytes;
	fprintf(arg.f, "SP: avg Down %.4f KB. \n", ((float)totalDownBytes)/1024/tmpTime);
	fprintf(arg.f, "SP: avg Up   %.4f KB. \n", ((float)totalUpBytes)/1024/tmpTime);

	arg.buf = buffer + sizeof (int);
	*(unsigned char *) (arg.buf) = RM2TS_STAT_QUERY;//RM2TS=0x20
	arg.buf += sizeof (unsigned char);
	querynum = (int *) (arg.buf);
	arg.buf += sizeof (int);

	arg.type = TYPE_CS;
	apply_session (TRACKER[TYPE_CS].head, TRACKER[TYPE_CS].maxid+1, apply_idle, &arg);
/*
	arg.type = TYPE_CP;
	apply_session (TRACKER[TYPE_CP].head, TRACKER[TYPE_CP].maxid+1, apply_idle, &arg);
*/
	apply_list (ChannelList, apply_check, &arg);
#ifdef TEST
	for (type=0; type<MAX_TS; type++)
	{
		if (arg.xml[type] == NULL)
			continue;
		fprintf (arg.xml[type], "</GaoV>\r\n");
		fclose (arg.xml[type]);
	}
#endif
	fprintf (arg.f, "Channel Count : %d.Total client : %d . Total upsize :%lldB . \n",
	 	arg.spchannelcount, arg.totalclient, arg.totalupsize);
		fprintf (arg.f,
	 	"\n************************END SnapShot*********************\n");
	fclose (arg.f);

    // added by lixingwu, 20070313
    // upload channel.xml to server, only edu
    char upload_cmd[MAX_DATA];
    sprintf(upload_cmd, "curl -F filename=@%s/%s/channel.xml -F domain=%s %s",
        WWW_ROOT, NET_NAME[EDUTS], spip[EDUTS], CAS_ADDR);
    printf("%s\n", upload_cmd);
    system(upload_cmd);

#ifdef TEST
	if (arg.spchannelcount > 0)
	{
		*querynum = arg.spchannelcount;
		*(int *) buffer = arg.buf - buffer;
		for (type=0; type<MAX_TS; type++)
		{
			if (tsSock[type] <= 0)
				continue;
			if (sendto (tsSock[type], buffer, *(int *) buffer, 0, (struct sockaddr *) &tsAddr[type], sizeof (struct sockaddr_in)) != *(int *) buffer)
			{
				PDEBUG ("sent to ts %d error. \n", type);
				return 0;
			}
		}
	}
#endif
	logto_xml (time_interval, tmpTime, arg.spchannelcount, arg.totalclient);
	lastCheck = CurrentTime;
	last_snapshot ++;
	tmpDownBytes = tmpUpBytes = 0;
	if (last_snapshot > SnapShotInterval)
	{
		system ("/usr/bin/vmstat -a >> sp.log 2>&1 &");
		last_snapshot = 0;
	}
	return 0;
}


int
main (int argc, char **argv)
{
	int i, mode = 1;

	if (argc < 2)
	{
		printf ("usage: %s mode(0 for daemon, 1 for console).\n",
			argv[0]);
		return -1;
	}
	signal (SIGPIPE, SIG_IGN);
	signal (SIGINT, terminate);
	signal (SIGHUP, hup_handler);

	mode = atoi (argv[1]);

	if (mode == 0)
		daemon (1, 1);

	read_config (CONFIG, ConfigParameters,
		     sizeof (ConfigParameters) / sizeof (struct NamVal));
	for (i = 0; i < 10 && IN_LOOP > 0; i++)
	{
		/*
		   pid_t pid;
		   if ((pid = fork ()) == 0)
		   {
		 */
		FD_ZERO (&osocks);
		if (init_sp () < 0)	// || initLOG () < 0)
		{
			PDEBUG ("init_sp error, exit...\n");
			exit (-1);
		}
		process_child ();
		/*
		   } else if (pid < 0)
		   {
		   perror ("fork");
		   exit (pid);
		   } else
		   {
		   waitpid (pid, NULL, 0);
		   }
		 */
	}
	return 0;
}

int
process_P2P_HELLO (struct Session *p, struct Message *m)
{
	struct SPUpdate spupdate;
	struct Edge *pedge;
	struct Channel *pc;
	int listnum;
	PDEBUG ("CP %d.%d.%d.%d:%d join channel %.32s\n",
		IPADDR (p->host), p->port, m->buffer);
	listnum = p - TRACKER[TYPE_CP].head;
	if ((pc = findChannel (m->buffer, MD5_LEN)) == NULL)
	{
		if ((pc = findOrder (m->buffer, MD5_LEN)) == NULL &&
			(pc = newOrder (m->buffer)) == NULL)
		{
			BUILD_NOCH_SPUPDATE(spupdate);
			send_P2P_SPUPDATE (p, pc, m->buffer, &spupdate);
			PDEBUG ("Cannot find channel %.32s\n",
				m->buffer);
			//Clientclosure (listnum, TYPE_CP);
		} else
		{
			BUILD_ORDER_SPUPDATE(spupdate,pc);
			send_P2P_SPUPDATE (p, pc, m->buffer, &spupdate);
			PDEBUG ("channel %.32s spupdate %d~%d. \n",
				m->buffer, spupdate.minBlockID,
				spupdate.maxBlockID);
		}
	} else
	{
		for (pedge = p->header; pedge && pedge->head == pc;
		     pedge = pedge->enext);
		if (!pedge)
		{
			pedge = newEdge (pc, p);
			pc->numclient++;
		}
		if (pc->pcinfo)
		{
			if (pc->pcinfo->status > 0)
			{
				// this channel has been closed
				BUILD_CLOSE_SPUPDATE(spupdate);
				send_P2P_SPUPDATE (p, pc, m->buffer, &spupdate);
				PDEBUG ( "channel %.32s has been closed. \n",
					m->buffer);
				//Clientclosure (listnum, TYPE_CP);
			} else if (pc->pcinfo->mlist != NULL)
			{
				BUILD_MLIST_SPUPDATE (spupdate, pc);
				send_P2P_SPUPDATE (p, pc, m->buffer, &spupdate);
				sendMedia (p, pc);
				PDEBUG ("Channel %.32s is a playlist.\n", m->buffer);
			} else
			{
				BUILD_LIVE_SPUPDATE(spupdate,pc);
				send_P2P_SPUPDATE (p, pc, m->buffer, &spupdate);
				sendMedia (p, pc);
			}
		} else
		{
			BUILD_ORDER_SPUPDATE(spupdate,pc);
			send_P2P_SPUPDATE (p, pc, m->buffer, &spupdate);
			PDEBUG ("channel %.32s spupdate %d~%d. \n",
				m->buffer, spupdate.minBlockID,
				spupdate.maxBlockID);
		}
	}
	return 0;
}

int
process_P2P_PUSHLIST (struct Session *p, struct Message *m)
{
	struct Edge *pedge;
	struct Channel *pc;
	char *buf;
	int i,size, type;

	if ((pc = findChannel (m->buffer, MD5_LEN)) == NULL)
		pc = newOrder (m->buffer);
	else if (pc->pcinfo != NULL)
	{
		for (pedge = p->header; pedge && pedge->head != pc;
		     pedge = pedge->enext);
		if (pedge == NULL)
		{
			pedge = newEdge (pc, p);
			pc->numclient++;
		}
	}
	if (pc == NULL)
		return -1;

	if (p->numjob >= MAX_JOB_PER_SESSION)
		return -2;

	buf = m->buffer + MD5_LEN;
	type = *(unsigned char *) buf;
	buf += sizeof (char);
	size = *(unsigned char *) buf;
	buf += sizeof (char);
	if (type)
	{
		deleteChannel (p, pc);
		for (i=0; i<size; i++)
			if (process_P2P_REQUEST_real (p, pc, ((unsigned int *)buf)[i]) < 0) 
				return -1;
	} else
	{
		for (i=0; i<size; i++)
			if (process_P2P_REQUEST_real (p, pc, ((unsigned int *)buf)[i]) < 0)
				return -1;
		buf += size * sizeof (int);
		size = *(unsigned char *) buf;
		buf += sizeof (char);
		deleteJob (p, pc, (unsigned int *) buf, size);
	}
	return 0;
}

int
process_P2P_REQUEST_real (struct Session *p, struct Channel *pc, int id)
{
	struct JobDes *pj = newJob ();
	char *buf, *buffer;
	int listnum, size=0, max;
	struct SPUpdate spupdate;

	if (pj == NULL) return -1;
	buffer = getJobBuffer (pj, &max);
	listnum = p - TRACKER[TYPE_CP].head;
	buf = buffer + sizeof (int);
	*(unsigned char *) buf = P2P_RESPONSE;
	buf += sizeof (char);
	memcpy (buf, pc->channel_md5, MD5_LEN);
	buf += MD5_LEN;

	if (pc->pcinfo == NULL)
	{
		*(int *) buf = id;
		buf += sizeof (int);
		if ((size =
		     locate_order_by_id (pc, id, buf + sizeof (int),
					 max)) < 0 && size != -2)
		{
			// BLOCK NOT FOUND
			spupdate.minKeySample = -1LL;
			spupdate.maxKeySample = -1LL;
			spupdate.minBlockID = 0xffffffff;
			spupdate.maxBlockID = 0xffffffff;
			send_P2P_SPUPDATE (p, pc, pc->channel_md5, &spupdate);
			size = 0;
			*(int *) buf = 0;
			buf += sizeof (int);

		} else if (size == -2)
		{
			PDEBUG ("Leave blocks %d to next round.\n", id);
			return -1;
		} else
		{
			// block found
			*(int *) buf = size;
			buf += sizeof (int) + size;
			p->last_transferblock = CurrentTime;
		}
//              Clientclosure (listnum, TYPE_CP);
	} else if (id >= 0 && pc->pcinfo->mlist != NULL
		   && (size = locate_mplist_by_id (pc, id, buf, max - 32)) > 0)
	{
		p->last_transferblock = CurrentTime;
		buf += 2 * sizeof (int) + size;

	} else if (id >= 0 && pc->pcinfo->mlist == NULL
		   && (size = locate_by_id (pc, id, buf, max - 32)) > 0)
	{
		p->last_transferblock = CurrentTime;
		buf += 2 * sizeof (int) + size;
	} else if (size == -2)
	{
		assert (0);
		PDEBUG ("Leave blocks %d to next round.\n", id);
		return -1;
	} else
	{
		*(int *) buf = id;
		buf += sizeof (int);
		size = 0;
		*(int *) buf = 0;
		buf += sizeof (int);
		PDEBUG ("Cannot find block id %d required by client %d.%d.%d.%d.\n",
			id, IPADDR (p->host));
	}
	*(int *) buffer = buf - buffer;
	setblockId (pj, id);
	writeDATAMessage (p, pc, pj);
//	PDEBUG ("Write block %d to %d.%d.%d.%d\n", id,
//		IPADDR (p->host));
	return 0;
}


int
init_CP (int listnum)
{
	return 0;
}

int
process_CP (int listnum)
{
	int ret;
	struct Session *p = &(TRACKER[TYPE_CP].head[listnum]);
	struct Message *m = (struct Message *) (p->buf + p->start);

	tmpDownBytes += m->len;

	switch (m->type)
	{
		case P2P_HELLO:
			ret=  process_P2P_HELLO (p, m);
			break;
		case P2P_PUSHLIST:
			ret = process_P2P_PUSHLIST (p, m);
			break;
		case P2P_MSG:
			break;
		default:
			ret = -1;
			break;
	}
	switch (ret)
	{
		case -1:
			PDEBUG ("Message processing error from client %d.%d.%d.%d\n",
				IPADDR (p->host));
			Clientclosure (listnum, TYPE_CP);
			return -1;
		case -2:
			return -2;
		default:
			return 0;
	}
}

int
closure_CP (int listnum)
{
	struct Session *p = &(TRACKER[TYPE_CP].head[listnum]);
//      struct Channel *pc = p->pc;
	struct Edge *pedge, *nextedge;
	PDEBUG ("CP disconnected from %d.%d.%d.%d:%d\n",
		IPADDR (p->host), p->port);
	for (pedge = p->header; pedge; pedge = nextedge)
	{
		nextedge = pedge->enext;
		if (pedge->head)
			pedge->head->numclient--;
		delEdge (pedge);
	}
	FD_CLR (p->socket, &osocks);
	close (p->socket);
	FREE (p->buf);
	deleteAll (p);
	memset (p, 0, sizeof (struct Session));
	return 0;
}


int
init_CS (int listnum)
{
	return 0;
}


int
process_CS2SP_REGISTER (int listnum, char *msg)
{
	int i, errmsg;
	char cname[MAX_LINE], cmd5[MD5_LEN + 1], buffer[MAX_DATA];
	//char escape_buf[MAX_LINE];
	char md5[MD5_LEN + 1];
	struct Session *p = &(TRACKER[TYPE_CS].head[listnum]), *source;
	struct Channel *pc;
	float bitrate, limitedbitrate=10000.0;
	int id, startblock, size, maxblocksize, datalen, issave=0;

	size = *(unsigned char *) msg;
	msg += sizeof (char);
	if (size > sizeof (cname) || size <= 0)	//wrong Message
	{
		Clientclosure (listnum, TYPE_CS);
		return -1;
	} else
		memcpy (cname, msg, size);
	cname[size] = 0;
	msg += size;
//      memcpy (cmd5, msg, MD5_LEN);
//      msg += MD5_LEN;
	id = *(int *) msg;
	msg += sizeof (int);

	sprintf (buffer, "%d@%s_%s", id, defaultspip, cname);
	md5_calc ((unsigned char *) md5, (unsigned char *) buffer,
		  strlen (buffer));
	for (i = 0; i < MD5_LEN; i += 2)
		sprintf (cmd5 + i, "%02x", (unsigned char) md5[i / 2]);
	cmd5[MD5_LEN] = 0;

	memcpy (md5, msg, MD5_LEN);
	msg += MD5_LEN;
	maxblocksize = *(int *) msg;
	msg += sizeof (int);
	bitrate = *(float *) msg;
	msg += sizeof (float);
	datalen = *(unsigned short *) msg;
	if (datalen > MAX_LINE)
	{
		Clientclosure (listnum, TYPE_CS);
		return -1;
	}
	msg += sizeof (short);
	if (AuthCS && (errmsg =
	     isAllowed (id, md5, cname, bitrate, &limitedbitrate, &issave)) < 0)
	{
		PDEBUG ("User %d is not allowed to newchannel %s.\n",
			id, cname);
		send_p2p_err (p, -errmsg, 1);
		Clientclosure (listnum, TYPE_CS);
		return -1;
	}

	startblock = 0;
	if ((pc = findChannel (cmd5, MD5_LEN)) != NULL)
	{
		if (pc->pcinfo == NULL || pc->pcinfo->mlist != NULL)
		{
			PDEBUG ("The channel %s is a playlist.\n", cname);
			send_p2p_err (p, ERR_INTERNAL, 1);
			Clientclosure (listnum, TYPE_CS);
			return -1;
		}
		if ((source = pc->pcinfo->dataSource) != NULL)
		{
			Clientclosure (source - TRACKER[TYPE_CS].head,
				       TYPE_CS);
		}
	}
	if ((pc = newLiveChannel (cname, p, cmd5, bitrate,
				    maxblocksize)) != (struct Channel *) 0)
	{
		p->pc = pc;
		pc->pcinfo->userid = id;
		pc->pcinfo->limitedBitRate = limitedbitrate;
		pc->pcinfo->isSave = issave;
		pc->pcinfo->dataSource = &(TRACKER[TYPE_CS].head[listnum]);
		pc->pcinfo->startid = pc->pcinfo->maxID = (CurrentTime - FIX_MAGIC) * 16;
	} else
	{
		PDEBUG ("newLiveChannel failed.\n");
		send_p2p_err (p, ERR_INTERNAL, 1);
		Clientclosure (listnum, TYPE_CS);
		return -1;
	}
	*(int *) buffer = 9;
	*(char *) (buffer + sizeof (int)) = SP2CS_WELCOME;
	*(int *) (buffer + sizeof (int) + sizeof (char)) = startblock;
	if (writeMessage (p, pc, buffer) < 0)
	{
		send_p2p_err (p, ERR_INTERNAL, 1);
		Clientclosure (listnum, TYPE_CS);
		return -1;
	}
	pc->pcinfo->cur_channel = pc->pcinfo->max_channel = 1;
	pc->pcinfo->media = calloc (1, sizeof (struct MediaData));
	pc->pcinfo->media[0].start = 0;
	pc->pcinfo->media[0].len = -1;
	pc->pcinfo->media[0].dlen = datalen;
	pc->pcinfo->media[0].data = calloc (1, datalen);
	memcpy (pc->pcinfo->media[0].data, msg, datalen);
#ifdef TEST
	/*
	for (type = 0; type < MAX_TS; ++type)
		buildGTV (pc, datalen, msg, type);
	*/
	for (i=0; i<MAX_TS; i++) {
	if (buildGTV (pc, datalen, msg, i) < 0)
		continue;
	}
#endif
#ifdef HAVE_MYSQL
//      query_mysql (local_mysql, "delete from channel where ChannelMD5 = \"%s\"", cmd5);
//      mysql_escape_string (escape_buf, msg, datalen);
//      query_mysql (local_mysql, "insert into channel (ChannelName, ChannelBitrate, ChannelAttachData, ChannelMD5, ChannelOwnerID) values (\"%s\",\"%f\", \"%s\", \"%s\", \"%d\")", cname, bitrate, escape_buf, cmd5, id);
#endif
	return 0;
}

int
process_CS2SP_UPDATE (int listnum, float rate)	//only update bitrate
{
	struct Session *p = &(TRACKER[TYPE_CS].head[listnum]);
	struct Channel *pc = p->pc;
	struct LiveChannelInfo *pcinfo = pc->pcinfo;

	pcinfo->bitrate = rate;
	if (rate > pcinfo->limitedBitRate)
	{
		send_p2p_err (p, ERR_EXCEED_BITRATE, 1);
		Clientclosure (listnum, TYPE_CS);
		return -1;
	}
#ifdef HAVE_MYSQL
//      query_mysql (local_mysql, "update channel set ChannelBitrate = \"%f\" where ChannelMD5 = \"%s\"", rate, pc->channel_md5);
#endif

	return 0;
}

int
process_CS2SP_BLOCK (int listnum, char *msg)
{
	char *buf, buffer[MAX_DATA];
	struct Edge *pedge;
	int size;		// max=TRACKER[TYPE_CP].maxid+1;
	struct Session *p = &(TRACKER[TYPE_CS].head[listnum]);
//      struct Session *q=TRACKER[TYPE_CP].head;
	struct Channel *pc = p->pc;
	struct LiveChannelInfo *pcinfo;

	if (p->pc == NULL || (pcinfo = p->pc->pcinfo) == NULL || pcinfo->mlist != NULL)
	{
		PDEBUG ("Unmatched channel\n");
		Clientclosure (listnum, TYPE_CS);
		return -1;
	}

	pcinfo = p->pc->pcinfo;
	if ((size = saveBlock (pc, msg, p)) > 0)
	{
		// directly send this block to connected CPs
		buf = buffer + sizeof (int);
		*(unsigned char *) buf = P2P_RESPONSE;
		buf += sizeof (char);
		memcpy (buf, pc->channel_md5, MD5_LEN);
		buf += MD5_LEN;
		p->last_transferblock = CurrentTime;
		memcpy (buf, msg, size + 2 * sizeof (int));
		buf += size + 2 * sizeof (int);
		*(int *) buffer = buf - buffer;
		for (pedge = pc->PeerHead; pedge; pedge = pedge->cnext)
		{
			if (pedge->me->numjob >= MAX_JOB_PER_SESSION)
				continue;
			pedge->me->last_transferblock = CurrentTime;
//			PDEBUG ("write %u ",
//				((unsigned int *) msg)[0]);
			if (-1 == writeMessage (pedge->me, pc, buffer))
				PDEBUG ("buffer is full\n");
//			else
//				PDEBUG ("OK\n");
		}
		if (pcinfo->updated <= CurrentTime + SPUPDATE_SLOT)	//pcinfo is livechannel
			send_all_spupdate (pc, &(pcinfo->s));
	} else
	{
		PDEBUG ("save Block Failed ! size %d, %d\n", size,
			listnum);
		Clientclosure (listnum, TYPE_CS);
	}
	return 0;
}

int
process_CS (int listnum)
{
	struct Session *p = &(TRACKER[TYPE_CS].head[listnum]);
	struct Message *m = (struct Message *) (p->buf + p->start);

	tmpDownBytes += m->len;

	switch (m->type)
	{
		case CS2SP_REGISTER:
			process_CS2SP_REGISTER (listnum, m->buffer);
			break;
		case CS2SP_UPDATE:
			process_CS2SP_UPDATE (listnum,
					      *(float *) (m->buffer));
			break;
		case CS2SP_BLOCK:
			process_CS2SP_BLOCK (listnum, m->buffer);
			break;
		default:
			Clientclosure (listnum, TYPE_CS);
			return -1;
	}
	return 0;
}

int
closure_CS (int listnum)
{
	struct Session *p = &(TRACKER[TYPE_CS].head[listnum]);
	struct Channel *pc = p->pc;
	PDEBUG ("CS disconnected from %d.%d.%d.%d:%d\n",
		IPADDR (p->host), p->port);

	if (pc)
	{
		if (pc->pcinfo)
		{
			pc->pcinfo->dataSource = NULL;
			pc->pcinfo->status = 1;
		}
		freeLiveChannel (pc, NULL);
	}
	FD_CLR (TRACKER[TYPE_CS].head[listnum].socket, &osocks);
	close (TRACKER[TYPE_CS].head[listnum].socket);
	FREE (p->buf);
	deleteAll (p);
	memset (&(TRACKER[TYPE_CS].head[listnum]), 0,
		sizeof (struct Session));
	return 0;
}


int
init_sp ()
{
	FILE *pidf;
	struct rlimit rl;
	char *index;
	int type;
	char buffer[MAX_DATA];

	rl.rlim_cur = rl.rlim_max = 1000000;
	if (setrlimit (RLIMIT_NOFILE, &rl) != 0)
	{
		perror ("setrlimit");
	}
	for (type=0; type<MAX_TS; type++)
	{
		if (spip[type] != NULL && strlen (spip[type]) >= MIN_IPADDR_LEN)
		{
			defaultspip = spip[type];
			break;
		}
	}
	if (defaultspip == NULL)
		defaultspip = "127.0.0.1";

	OPENLOG;
#ifdef DEBUG
	system ("ulimit -a");
	if (getrlimit (RLIMIT_CORE, &rl) != 0)
	{
		perror ("getrlimit");
	}
	PDEBUG ("Get core limit %d:%d\n", (int)rl.rlim_cur, (int)rl.rlim_max);
	rl.rlim_cur = rl.rlim_max = (rlim_t )10240000;
	if (setrlimit (RLIMIT_CORE, &rl) != 0)
	{
		perror ("setrlimit");
	}
	if (getrlimit (RLIMIT_CORE, &rl) != 0)
	{
		perror ("getrlimit");
	}
	PDEBUG ("Set core limit to %d:%d\n", (int)rl.rlim_cur, (int)rl.rlim_max);
	system ("ulimit -a");
#endif
#ifdef HAVE_MYSQL
	if ((local_mysql =
	     init_mysql (MYSQL_HOST, MYSQL_USER, MYSQL_PASS, MYSQL_DB,
			 "/var/run/mysqld/mysqld.sock")) == 0)
	{
		PDEBUG ("Error in init_mysql.\n");
		exit (1);
	}
#endif
	TRACKER[TYPE_CP].type = TYPE_CP;	//allocate CP ServerDesc  TYPE_CP = 0
	TRACKER[TYPE_CP].port = SP4CP_PORT;	//SP4CP_PORT = 50001
	TRACKER[TYPE_CP].cur = 0;	//current client connection 
	TRACKER[TYPE_CP].max = MAX_CP;	//MAX_CP = 2048
	TRACKER[TYPE_CP].init = init_CP;	//the function pointer of init_CP,return a debug message
	TRACKER[TYPE_CP].process = process_CP;	//the function pointer of process_CP, switch TYPE
	TRACKER[TYPE_CP].closure = closure_CP;	//the funciont pointer of closure_CP,return debug msg and freejob
	TRACKER[TYPE_CP].head = calloc (sizeof (struct Session), TRACKER[TYPE_CP].max);	//allocate session memory
	switch (BINDALL)
	{
		case 0:
			if ((TRACKER[TYPE_CP].sock = init_server (spip[0], SP4CP_PORT)) < 0)	//PORT = 50001
				return -1;
			break;
		default:
			if ((TRACKER[TYPE_CP].sock = init_server (NULL, SP4CP_PORT)) < 0)	//PORT = 50001
				return -1;
			break;
	}
	FD_SET (TRACKER[TYPE_CP].sock, &osocks);
	TRACKER[TYPE_CS].type = TYPE_CS;	//allocate CP ServerDese,TYPE_CS = 1
	TRACKER[TYPE_CS].port = SP4CS_PORT;
	TRACKER[TYPE_CS].cur = 0;
	TRACKER[TYPE_CS].max = MAX_CS;	//MAX_CS = 512
	TRACKER[TYPE_CS].init = init_CS;	//return a debug message        
	TRACKER[TYPE_CS].process = process_CS;
	TRACKER[TYPE_CS].closure = closure_CS;
	TRACKER[TYPE_CS].head =
		calloc (sizeof (struct Session), TRACKER[TYPE_CS].max);
	switch (BINDALL)
	{
		case 0:
			if ((TRACKER[TYPE_CS].sock = init_server (spip[0], SP4CS_PORT)) < 0)	//port = 
				return -1;
			break;
		default:
			if ((TRACKER[TYPE_CS].sock = init_server (NULL, SP4CS_PORT)) < 0)	//port = 
				return -1;
			break;
	}
	FD_SET (TRACKER[TYPE_CS].sock, &osocks);
	if (db_init (Home, Database) < 0)
		return -1;

	memset (tsAddr, 0, sizeof (struct sockaddr_in) * MAX_TS);	//sockaddr_in type
	for (type = 0; type < MAX_TS; ++type)
	{
#ifdef TEST
		sprintf (buffer, "rm -f %s/%s/*.gtv", WWW_ROOT,
			 NET_NAME[type]);
		system (buffer);
		sprintf (buffer, "rm -f %s/%s/*.mediadata", WWW_ROOT,
			 NET_NAME[type]);
		system (buffer);
		sprintf (buffer, "rm -f %s/%s/channel.xml", WWW_ROOT,
			 NET_NAME[type]);
		system (buffer);
#endif
		sprintf (buffer, "rm -fr %s/%s/*", PREFIX, LIVE_PREFIX);
		system (buffer);

		if (tsip[type] == NULL) continue;
		tsAddr[type].sin_family = AF_INET;
		tsAddr[type].sin_port = htons (TS4RM_PORT);
		index = strchr (tsip[type], ':');
		if (index == NULL)
			inet_aton (tsip[type], &tsAddr[type].sin_addr);
		else
		{
			*index = 0;
			inet_aton (tsip[type], &tsAddr[type].sin_addr);
			*index = ':';
		}
		tsSock[type] = socket (PF_INET, SOCK_DGRAM, 0);	//upd connection
		if (tsSock[type] < 0)
			return -1;
	}
	mkdir (PREFIX, 0777);
	sprintf (buffer, "%s/%s", PREFIX, LIVE_PREFIX);
	mkdir (buffer, 0777);
	sprintf (buffer, "%s/%s", PREFIX, ORDER_PREFIX);
	mkdir (buffer, 0777);
	sprintf (buffer, "%s/%s", PREFIX, PLIST_PREFIX);
	mkdir (buffer, 0777);
	sprintf (buffer, "%s/%s", PREFIX, PROG_PREFIX);
	mkdir (buffer, 0777);
	if ((pidf = fopen (PIDFile, "w")) == NULL)
	{
		PDEBUG ("Cannot open pidfile.\n");
		return -1;
	}
	fprintf (pidf, "%d\n", getpid ());
	fclose (pidf);
	return 0;
}

#ifdef TEST
/*
int
buildMediaData (struct Channel *pc, int datalen, char *data, int type)
{
	int i;
	char olddata[MAX_DATA];
	char buffer[MAX_DATA];
	struct stat stbuf;
	FILE *f;
	assert (pc->pcinfo);
	snprintf (buffer, MAX_LINE, "%s/%s/%s.mediadata", WWW_ROOT, NET_NAME[type], pc->channel_name);
	if (stat (buffer, &stbuf) == 0)
	{
		if (stbuf.st_size != datalen)
		{
			PDEBUG ("old media data size %d not match new %d\n", (int)(stbuf.st_size), datalen);
			return -1;
		}
		if ((f = fopen (buffer, "r")) == NULL)
		{
			PDEBUG ("cannot open mediadata file %s\n", buffer);
			perror ("fopen");
			return -1;
		}
		if (fread (olddata, datalen, 1, f) == 1)
		{
			for (i=0; i<datalen; i++)
			{
				if (data[i] != olddata[i])
				{
					PDEBUG ("media data not match %s, %d\n", buffer, i);
					fclose (f);
					return -1;
				}
			}
		} else
		{
			PDEBUG ("Error in read old mediadata %s.\n", buffer);
			fclose (f);
			return -1;
		}
		fclose (f);
		return 0;
	} else if ((f = fopen (buffer, "w")) == NULL)
	{
		PDEBUG ("cannot open gtv file %s\n", buffer);
		perror ("fopen");
		return -1;
	}
	fwrite (data, datalen, 1, f);
	fclose (f);
	return 0;
}
*/

int
buildGTV (struct Channel *pc, int datalen, char *data, int type)
{
	char buffer[MAX_DATA];
	FILE *f;
	assert (pc->pcinfo);
	snprintf (buffer, MAX_LINE, "%s/%s/%s.gtv", WWW_ROOT, NET_NAME[type], pc->channel_name);
//	snprintf (buffer, MAX_LINE, "%s/%s.gtv", pc->fname, pc->channel_name);
	if ((f = fopen (buffer, "w")) == NULL)
	{
		PDEBUG ("cannot open gtv file %s\n", buffer);
		perror ("fopen");
		return -1;
	}
	if (pc->pcinfo)
	{
		if (pc->pcinfo->mlist != NULL)
		sprintf (buffer,
			 "CSUserID=%d\r\nBlockSize=%d\r\nBitRate=%f\r\nChannelName=%s\r\nPlaylist=true\r\nResourceHash=%s\r\nTrackServer=%s\r\nSuperPeer=%s:50001\r\nDataLength=%d\r\nData=",
			 pc->pcinfo->userid, pc->maxblocksize, pc->pcinfo->bitrate,
			 pc->channel_name, pc->channel_md5, tsip[type], spip[type],
			 datalen);
		else
		sprintf (buffer,
			 "CSUserID=%d\r\nBlockSize=%d\r\nBitRate=%f\r\nChannelName=%s\r\nResourceHash=%s\r\nTrackServer=%s\r\nSuperPeer=%s:50001\r\nDataLength=%d\r\nData=",
			 pc->pcinfo->userid, pc->maxblocksize, pc->pcinfo->bitrate,
			 pc->channel_name, pc->channel_md5, tsip[type], spip[type],
			 datalen);
	} else
		sprintf (buffer,
			 "GTVHome=%s/\r\nBlockSize=%d\r\nResourceHash=%s\r\nTrackServer=%s\r\nSuperPeer=%s:50001\r\nDataLength=%d\r\nData=",
			 urlroot, pc->maxblocksize, pc->channel_md5,
			 tsip[type], spip[type], datalen);
	fwrite (buffer, strlen (buffer), 1, f);
	fwrite (data, datalen, 1, f);
	fclose (f);
/*	snprintf (buffer, MAX_LINE, "%s/%s/%s.mediadata", WWW_ROOT, NET_NAME[type], pc->channel_name);
	if (stat (buffer, &stbuf) == 0)
	{
		if (stbuf.st_size != datalen)
		{
			PDEBUG ("old media data size %d not match new %d\n", (int)(stbuf.st_size), datalen);
			return -1;
		}
		if ((f = fopen (buffer, "r")) == NULL)
		{
			PDEBUG ("cannot open mediadata file %s\n", buffer);
			perror ("fopen");
			return -1;
		}
		if (fread (olddata, datalen, 1, f) == 1)
		{
			for (i=0; i<datalen; i++)
			{
				if (data[i] != olddata[i])
				{
					PDEBUG ("media data not match %s, %d\n", buffer, i);
					fclose (f);
					return -1;
				}
			}
		} else
		{
			PDEBUG ("Error in read old mediadata %s.\n", buffer);
			fclose (f);
			return -1;
		}
		fclose (f);
		return 0;
	} else if ((f = fopen (buffer, "w")) == NULL)
	{
		PDEBUG ("cannot open gtv file %s\n", buffer);
		perror ("fopen");
		return -1;
	}
	fwrite (data, datalen, 1, f);
	fclose (f);
*/
    // added by lixingwu, 20070313
    // upload gtv files to server
    char upload_cmd[MAX_DATA];
    sprintf(upload_cmd, "curl -F filename=@%s/%s/%s.gtv -F domain=%s %s",
        WWW_ROOT, NET_NAME[type], pc->channel_name, spip[type], CAS_ADDR);
    printf("%s\n", upload_cmd);
    system(upload_cmd);
    
    return 0;
}
#endif

int
send_P2P_SPUPDATE (struct Session *p, struct Channel *pc, char *md5, struct SPUpdate *s)
{
	char buffer1[MAX_DATA];
	char *buf;
	buf = buffer1 + sizeof (int);
	*(unsigned char *) buf = P2P_SPUPDATE;	//P2P_SPUPDATE=1 in ProTocol file
	buf += sizeof (char);
	memcpy (buf, md5, MD5_LEN);
	buf += MD5_LEN;
	memcpy (buf, s, sizeof (struct SPUpdate));
	buf += sizeof (struct SPUpdate);
	*(int *) buffer1 = buf - buffer1;
	if (writeMessage (p, pc, buffer1) < 0)
		return -1;
	return 0;
}

int
send_p2p_err (struct Session *p, unsigned short code, int quit)
{
	char buffer1[MAX_DATA];
	char *buf;
	buf = buffer1 + sizeof (int);
	*(unsigned char *) buf = P2P_MSG;
	buf += sizeof (char);
	(*(unsigned short *) buf) = code;
	buf += sizeof (short);
	*(int *) buf = quit;
	buf += sizeof (int);
	*(int *) buffer1 = buf - buffer1;
	PDEBUG ("Send error msg type %hd to %p\n", code, p);
	if (writeMessage (p, NULL, buffer1) < 0)
		return -1;
	return 0;
}

void apply_idle (struct Session *p, void *arg)
{
	struct Argument *parg = (struct Argument *)arg;

	if (CurrentTime - p->last_transferblock >= MAX_TRANSFER_IDLE)
	{
		fprintf (parg->f, "%s Session timeout! %ld \n", parg->type == TYPE_CS? "CS":"CP", CurrentTime - p->last_transferblock);
		Clientclosure (p - TRACKER[parg->type].head, parg->type);
	}
}

void apply_check (struct Channel *p, void *arg)
{
	struct Argument *parg = (struct Argument *)arg;
	int type;
	struct LiveChannelInfo *pc = p->pcinfo;
	if (pc && pc->status <= 0)
	{
#ifdef HAVE_MYSQL
		query_mysql (local_mysql,
			     "insert into channel(ChannelName, ChannelBitrate, ChannelMD5, ChannelElapsed, ChannelRange) values ('%s\','%f','%d','%d')",
			     pc->channel_name, pc->bitrate,
			     p->channel_md5,
			     time (NULL) - p->ctime,
			     pc->s.maxKeySample -
			     pc->s.minKeySample);
#endif
#ifdef TEST
	for (type=0; type<MAX_TS; type++)
	{
		if (parg->xml[type] == NULL)
			continue;
		fprintf (parg->xml[type], "<Channel Name=\"%s\" Desc=\"%s\" File=\"%s.gtv\" NumClient=\"%d\" BitRate=\"%d\" Start=\"%ld\" End=\"-1\" Elapsed=\"%ld\"/>\r\n",
				 p->channel_name, "gtv", //pc->userid,
				 p->channel_name, p->numofnp[type],
				 (int) (pc->bitrate * 8),
				 (long) p->ctime, (long) CurrentTime);
	}
#endif
		PINFO ("query %s->%s \n",
				p->channel_name, p->channel_md5);
		memcpy (parg->buf, p->channel_md5, MD5_LEN);
		parg->buf += MD5_LEN;
		fprintf (parg->f, "Channel %s have %d client. upsize %lldB, avg speed %f; downsize %lldB, avg speed %f, reported %f, real/reported is %f%%.\n",
				 p->channel_md5, p->numclient,
				 p->upsize, ((float)(p->upsize)) / (CurrentTime - lastCheck), p->downsize, ((float)(p->downsize)) / (CurrentTime - lastCheck), p->pcinfo !=NULL ? p->pcinfo->bitrate:0, p->pcinfo != NULL && p->pcinfo->bitrate != 0 ? (((float)(p->downsize)) / (CurrentTime - lastCheck)*100/p->pcinfo->bitrate): 0);
		fprintf (parg->f, "Live SPUpdate : SampleMin %lld SampleMax %lld BlockMin %d BlockMax %d \n",
					 pc->s.minKeySample,
					 pc->s.maxKeySample,
					 pc->s.minBlockID,
					 pc->s.maxBlockID);
		parg->spchannelcount ++;
		parg->totalclient += p->numclient;
		parg->totalupsize += p->upsize;
	} else if (pc && pc->isSave == 0)
		freeLiveChannel (p, NULL);
}
